package flatlandAgent;

import java.io.File;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.SourceDataLine;

import neuralNetwork.NeuralNetwork;
import neuralNetwork.NeuralNetwork.ArrayOperations;
import evolutionaryProblems.RunPhenotype;
import evolutionaryProblems.RunningStatus;
import genotype.FlatlandGenotype;

public class RunFlatlandPhenotype extends RunPhenotype{
	
	private FlatlandPhenotype phenotype; 
//	private volatile RunningStatus status;
	private FlatlandGameModel flatlandGameModel; 
	private FlatlandGame game; 
	private NeuralNetwork network; 
	private int mapNr = 0;
	private FlatlandMap[] maps;
	private int iterations = -1;
	private int iteration; 
	
	private AudioFormat audioFormat;
	private AudioInputStream audioInputStream;
	private SourceDataLine sourceDataLine;
	private boolean stopPlayback = false;
	
	public RunFlatlandPhenotype(FlatlandGameModel flatlandGameModel, FlatlandPhenotype phenotype){
		status = RunningStatus.PAUSED; 
		this.flatlandGameModel = flatlandGameModel; 
		this.phenotype = phenotype; 
		setup(); 
	}
	
//	@Override
//	public void run() {
//		status = RunningStatus.RUNNING; 
//		while (status == RunningStatus.RUNNING) {
//			doTimeStep(); 
//		}
//		status = RunningStatus.FINISHED; 
//	}
	
	private void setup(){
//		FlatlandPhenotype fPhenotype = (FlatlandPhenotype) phenotype;
		maps = ((FlatlandFitnessFunction) phenotype.fitnessFunction).getMaps();
		mapNr = 0; 
	}
	
	@Override
	protected void doTimeStep(){
		checkSetup(); 
		if (status == RunningStatus.TERMINATING) {
			return; 
		}
		boolean[] foodSensors = game.getFoodSensors();
		boolean[] poisonSensors = game.getPoisonSensors(); 
		boolean[] sensors = new boolean[foodSensors.length + poisonSensors.length]; 
		int foodIndex = 0; 
		int poisonIndex = foodSensors.length; 
		ArrayOperations.insertData(sensors, foodSensors, foodIndex); 
		ArrayOperations.insertData(sensors, poisonSensors, poisonIndex); 
		double[] outputs = network.update(sensors); 
		FlatlandModel.Action action = selectAction(outputs); 
		try {
			synchronized (this) {
				wait(flatlandGameModel.getTimePerFrame());
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
			status = RunningStatus.FINISHED; 
		}
		int move = game.move(action);
		
		if(move == -1) {
			playAudio("no.wav");
		}
		
		
//		System.out.println("x: " + game.getPosX() + " y: " + game.getPosY()); 
	}
	

	private void checkSetup() { 
		if (++iteration > iterations){
			iteration = 0; 
			if (maps.length == mapNr) {
				status = RunningStatus.TERMINATING; 
				return; 
			}
			game = new FlatlandGame(maps[mapNr++]); 
			game.setDraw(true);
			flatlandGameModel.setMap(game.getGamePanel());
			flatlandGameModel.pack(); 
			Double[] weights = ((FlatlandGenotype)phenotype.getGenotype()).getArrayGenotype(); 
			network = new NeuralNetwork(((FlatlandGenotype) phenotype.getGenotype()).getNeuronsPerLayer());
			network.setWeights(convertDtod(weights)); 
			iterations = flatlandGameModel.getIterationsPerMap();
		}
	}

	private FlatlandModel.Action selectAction(double[] outputs){
		int maxIndex = 0; 
		double max = outputs[0]; 
		for (int i = 0; i < outputs.length; i++){
			if (outputs[i] > max){
				maxIndex = i; 
				max = outputs[i]; 
			}
		}
		switch (maxIndex) {
		case 0:
			return FlatlandModel.Action.MOVE_LEFT;
		case 1: 
			return FlatlandModel.Action.MOVE_FORWARD; 
		default:
			return FlatlandModel.Action.MOVE_RIGHT;
		}
		
	}
	
//	public double[] convertDtod(Double[] list){
//		double[] convert = new double[list.length]; 
//		for (int i = 0; i < convert.length; i++) {
//			convert[i] = list[i]; 
//		}
//		return convert; 
//	}
//	
//	public void kill(){
//		if (status == RunningStatus.FINISHED){
//			return; 
//		}
//		status = RunningStatus.TERMINATING; 
//	}
//	
//	public boolean haveKilled(){
//		return status == RunningStatus.FINISHED; 
//	}
//
//	public void pause(boolean pause) {
//		if (pause) {
//			status = RunningStatus.PAUSED; 
//		} else {
//			new Thread(this).start();  
//		}
//	}
	private void playAudio(String filename) {
		try {
			String path = new File("").getAbsolutePath();
			FileSystem fileSys = FileSystems.getDefault();
			String sep = fileSys.getSeparator();
			path += (sep + "src" + sep + "audio" + sep);
			
			File soundFile = new File(path + filename);
			audioInputStream = AudioSystem.getAudioInputStream(soundFile);
			audioFormat = audioInputStream.getFormat();

			DataLine.Info dataLineInfo = new DataLine.Info(
					SourceDataLine.class, audioFormat);

			sourceDataLine = (SourceDataLine) AudioSystem.getLine(dataLineInfo);

			new PlayThread().start();
		} catch (Exception e) {
			e.printStackTrace();
			System.exit(0);
		}
	}

	class PlayThread extends Thread {
		byte tempBuffer[] = new byte[10000];

		public void run() {
			try {
				sourceDataLine.open(audioFormat);
				sourceDataLine.start();

				int cnt;
				
				while ((cnt = audioInputStream.read(tempBuffer, 0,
						tempBuffer.length)) != -1 && stopPlayback == false) {
					if (cnt > 0) {

						sourceDataLine.write(tempBuffer, 0, cnt);
					}
				}
				sourceDataLine.drain();
				sourceDataLine.close();

				stopPlayback = false;
			} catch (Exception e) {
				e.printStackTrace();
				System.exit(0);
			}
		}
	}
}